<?php
/**
 * App bootstrap file.
 *
 * @author Pavel <dew.pavel.wo@gmail.com>
 */

/**
 * Starts the session.
 */
session_start();

/**
 * This constant defines the framework installation directory.
 */
defined('APP_PATH') or define('APP_PATH', __DIR__);

/**
 * This constant defines whether the application should be in debug mode or not. Defaults to false.
 */
defined('APP_DEBUG') or define('APP_DEBUG', false);

/**
 * BaseApp is the core helper class for the framework.
 *
 * Do not use BaseApp directly. Instead, use its child class [[App]] where
 * you can customize methods of BaseApp.
 */
class BaseApp
{
	/**
	 * @var array class map used by the App autoloading mechanism.
	 * The array keys are the class names (without leading backslashes), and the array values
	 * are the corresponding class file paths (or path aliases). This property mainly affects
	 * how [[autoload()]] works.
	 * @see autoload()
	 */
	public static $classMap = array();

	/**
	 * @var array config
	 */
	public static $config = array();

	/**
	 * @var array registered path aliases
	 * @see getAlias()
	 * @see setAlias()
	 */
	public static $aliases = array();

	/**
	 * Initializes this application component.
	 * @param array
	 */
	public static function init( $config = array() ){
		self::$config = $config;
		self::$aliases = array(
			'@app'    => __DIR__,
			'@lib'    => __DIR__."/lib",
			'@models' => __DIR__."/lib/models",
			'@modules'=> __DIR__."/mod",
		);
	}

	/**
	 * Class autoload loader.
	 * This method is invoked automatically when PHP sees an unknown class.
	 * The method will attempt to include the class file according to the following procedure:
	 *
	 * 1. Search in [[classMap]];
	 * 2. It will attempt to include the file associated with the corresponding class name
	 *
	 * @param string $className the fully qualified class name
	 */
	public static function autoload($className)
	{
		if (isset(static::$classMap[$className])) {
			$classFile = static::$classMap[$className];
			if ($classFile[0] === '@') {
				$classFile = static::getAlias($classFile);
			}
		} else {
			if (preg_match("/Model$/", $className)) {
				$classFile = self::getAlias("@models/". reset(explode("Model", $className)) ).".php";
			}
			else {
				$classFile = self::getAlias("@lib/".$className).".php";
			}
		}

		include($classFile);

		if (APP_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && !trait_exists($className, false)) {
			die("Unable to find '$className' in file: $classFile");
		}
	}

	/**
	 * Translates a path alias into an actual path.
	 *
	 * The translation is done according to the following procedure:
	 *
	 * 1. If the given alias does not start with '@', it is returned back without change;
	 * 2. Otherwise, look for the longest registered alias that matches the beginning part
	 *    of the given alias. If it exists, replace the matching part of the given alias with
	 *    the corresponding registered path.
	 *
	 * For example, by default '@app' is registered as the alias to the framework directory,
	 * say '/path/to/app'. The alias '@app/web' would then be translated into '/path/to/app/web'.
	 *
	 * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config'
	 * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path.
	 * This is because the longest alias takes precedence.
	 *
	 * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced
	 * instead of '@foo/bar', because '/' serves as the boundary character.
	 *
	 * Note, this method does not check if the returned path exists or not.
	 *
	 * @param string $alias the alias to be translated.
	 * @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered.
	 * @see setAlias()
	 */
	public static function getAlias($alias)
	{
		if (strncmp($alias, '@', 1)) {
			// not an alias
			return $alias;
		}

		$pos = strpos($alias, '/');
		$root = $pos === false ? $alias : substr($alias, 0, $pos);

		if (isset(static::$aliases[$root])) {
			if (is_string(static::$aliases[$root])) {
				return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos);
			} else {
				foreach (static::$aliases[$root] as $name => $path) {
					if (strpos($alias . '/', $name . '/') === 0) {
						return $path . substr($alias, strlen($name));
					}
				}
			}
		}
	}

	/**
	 * Registers a path alias.
	 *
	 * A path alias is a short name representing a long path (a file path, a URL, etc.)
	 * For example, we use '@app' as the alias of the path to the framework directory.
	 *
	 * A path alias must start with the character '@' so that it can be easily differentiated
	 * from non-alias paths.
	 *
	 * Note that this method does not check if the given path exists or not. All it does is
	 * to associate the alias with the path.
	 *
	 * Any trailing '/' and '\' characters in the given path will be trimmed.
	 *
	 * @param string $alias the alias name (e.g. "@app"). It must start with a '@' character.
	 * It may contain the forward slash '/' which serves as boundary character when performing
	 * alias translation by [[getAlias()]].
	 * @param string $path the path corresponding to the alias. Trailing '/' and '\' characters
	 * will be trimmed. This can be
	 *
	 * @see getAlias()
	 */
	public static function setAlias($alias, $path)
	{
		if (strncmp($alias, '@', 1)) {
			$alias = '@' . $alias;
		}
		$pos = strpos($alias, '/');
		$root = $pos === false ? $alias : substr($alias, 0, $pos);
		if ($path !== null) {
			$path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
			if (!isset(static::$aliases[$root])) {
				if ($pos === false) {
					static::$aliases[$root] = $path;
				} else {
					static::$aliases[$root] = [$alias => $path];
				}
			} elseif (is_string(static::$aliases[$root])) {
				if ($pos === false) {
					static::$aliases[$root] = $path;
				} else {
					static::$aliases[$root] = [
						$alias => $path,
						$root => static::$aliases[$root],
					];
				}
			} else {
				static::$aliases[$root][$alias] = $path;
				krsort(static::$aliases[$root]);
			}
		} elseif (isset(static::$aliases[$root])) {
			if (is_array(static::$aliases[$root])) {
				unset(static::$aliases[$root][$alias]);
			} elseif ($pos === false) {
				unset(static::$aliases[$root]);
			}
		}
	}

}